{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = np.array([[1, 2], [2, 5], [3, 3], [4, 7], [5, 2]])#\n",
    "\n",
    "y = np.array([0, 1, 0, 1, 0])#TODO  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "knn = KNeighborsClassifier(n_neighbors=1)  #TODO"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  display: none;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  overflow: visible;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".estimator-table summary {\n",
       "    padding: .5rem;\n",
       "    font-family: monospace;\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       ".estimator-table details[open] {\n",
       "    padding-left: 0.1rem;\n",
       "    padding-right: 0.1rem;\n",
       "    padding-bottom: 0.3rem;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table {\n",
       "    margin-left: auto !important;\n",
       "    margin-right: auto !important;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(odd) {\n",
       "    background-color: #fff;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(even) {\n",
       "    background-color: #f6f6f6;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:hover {\n",
       "    background-color: #e0e0e0;\n",
       "}\n",
       "\n",
       ".estimator-table table td {\n",
       "    border: 1px solid rgba(106, 105, 104, 0.232);\n",
       "}\n",
       "\n",
       ".user-set td {\n",
       "    color:rgb(255, 94, 0);\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td.value pre {\n",
       "    color:rgb(255, 94, 0) !important;\n",
       "    background-color: transparent !important;\n",
       "}\n",
       "\n",
       ".default td {\n",
       "    color: black;\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td i,\n",
       ".default td i {\n",
       "    color: black;\n",
       "}\n",
       "\n",
       ".copy-paste-icon {\n",
       "    background-image: url();\n",
       "    background-repeat: no-repeat;\n",
       "    background-size: 14px 14px;\n",
       "    background-position: 0;\n",
       "    display: inline-block;\n",
       "    width: 14px;\n",
       "    height: 14px;\n",
       "    cursor: pointer;\n",
       "}\n",
       "</style><body><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=1)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>KNeighborsClassifier</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.7/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\" data-param-prefix=\"\">\n",
       "        <div class=\"estimator-table\">\n",
       "            <details>\n",
       "                <summary>Parameters</summary>\n",
       "                <table class=\"parameters-table\">\n",
       "                  <tbody>\n",
       "                    \n",
       "        <tr class=\"user-set\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_neighbors',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_neighbors&nbsp;</td>\n",
       "            <td class=\"value\">1</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('weights',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">weights&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;uniform&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('algorithm',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">algorithm&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;auto&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('leaf_size',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">leaf_size&nbsp;</td>\n",
       "            <td class=\"value\">30</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('p',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">p&nbsp;</td>\n",
       "            <td class=\"value\">2</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;minkowski&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric_params',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric_params&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_jobs',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_jobs&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "                  </tbody>\n",
       "                </table>\n",
       "            </details>\n",
       "        </div>\n",
       "    </div></div></div></div></div><script>function copyToClipboard(text, element) {\n",
       "    // Get the parameter prefix from the closest toggleable content\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;\n",
       "\n",
       "    const originalStyle = element.style;\n",
       "    const computedStyle = window.getComputedStyle(element);\n",
       "    const originalWidth = computedStyle.width;\n",
       "    const originalHTML = element.innerHTML.replace('Copied!', '');\n",
       "\n",
       "    navigator.clipboard.writeText(fullParamName)\n",
       "        .then(() => {\n",
       "            element.style.width = originalWidth;\n",
       "            element.style.color = 'green';\n",
       "            element.innerHTML = \"Copied!\";\n",
       "\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        })\n",
       "        .catch(err => {\n",
       "            console.error('Failed to copy:', err);\n",
       "            element.style.color = 'red';\n",
       "            element.innerHTML = \"Failed!\";\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        });\n",
       "    return false;\n",
       "}\n",
       "\n",
       "document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const paramName = element.parentElement.nextElementSibling.textContent.trim();\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;\n",
       "\n",
       "    element.setAttribute('title', fullParamName);\n",
       "});\n",
       "</script></body>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=1)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X, y)# "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "KÓ: \n"
     ]
    }
   ],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user)#TODO\n",
    "print(f\"KÓ: {'gG' if prediction[0] == 1 else ''}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjgAAAHJCAYAAACIU0PXAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUqNJREFUeJzt3XlcVPX+P/DXAYZhF2RJEAR33BAIVMJdQXPD9euCRWrulrnULa+Zlma7lreyjLBuIlmo1zaScC/9uaDllpZCmqKIgiDIMMDn9weXuY4MMAMzDHN8Pe+Dx3XO+ZzP+bzng83LM2eRhBACRERERDJiZe4BEBERERkbAw4RERHJDgMOERERyQ4DDhEREckOAw4RERHJDgMOERERyQ4DDhEREckOAw4RERHJDgMOERERyQ4DDsnKnj17IEkSAgICamzXt29fSJKEjRs3Nsi4jCEgIACSJEGSJGzevLnadocPH9a0kySpAUf4P5VjNaUnnnhCq05JkmBvb4/AwEAsWLAA165dM+n+Kx07dgzR0dFwdXXVjCMzM7NB9k1E1bMx9wCIyHCbNm3CxIkTda774osvGng05hUZGYk2bdoAALKzs3Ho0CGsXbsWSUlJOHjwYK1htz4KCgowYsQIZGVloW/fvvDz84MkSXBycjLZPolIPww4RBYmJCQEP/74I27cuAFPT0+tdaWlpfjyyy/RsWNHXLhwASqVyixjTEtLg1qtbpB9Pfnkk3jiiSc0r2/cuIEhQ4bg6NGjWLx4Mb7++muT7fvIkSO4evUqHnvsMXz++ecm2w8RGY5fURFZmMmTJ6O0tBRbtmypsm7nzp3Izs7G5MmTzTCy/2ndujUCAwPNsm9PT0+8/fbbAIDvvvvOpEHr77//BgC0atXKZPsgorphwCG6x82bN7FkyRJ06tQJTk5OaNKkCdq1a4fHH38chw8frtL+xo0bWLx4Mdq3bw87Ozu4ubnh0Ucfxb59+6q0rTw/6IknnsC1a9fw5JNPwtfXFzY2Nli7dq3eYxw5ciScnJx0fhX1xRdfQJIkTJo0qcY+Dh48iJiYGHh6ekKpVCIgIABz5szB1atXtdq9/fbbkCQJzz//fLV9DRkyBJIkITU1VbOspnNwMjMzMXPmTAQEBECpVMLT0xNjx47Fb7/9VuOYDRESEgIAKC4uRk5ODoD/nbOzZ88e/Pjjj+jXr5/mvJm8vDzNtt988w0GDRoEd3d32NnZoV27dnjxxRdx584drRokSUJcXBwAYMWKFZrzb+49mgQABw4cwKhRo+Dl5aV5r59++mncuHGjyriNOcZKleebZWZmYvv27ejRowccHR3RtGlTTJw4URPS7ieEwKZNmzBgwADNflq1aoVJkybh559/rtL+5MmTiI2NRfPmzaFUKuHj44MpU6bwfCQyH0EkI7t37xYAhL+/f43t+vTpIwCIhIQEzbKCggLRpk0bAUC0bdtWjB49WowePVqEhYUJGxsb8dJLL2n1cfbsWdG8eXMBQLRu3VqMGjVK9O7dW9ja2gorKyuxadMmnWMbMmSI8PX1Fc2aNRNjx44Vw4YNEx999FGttfn7+wsAIisrS0yePFkAEH/++adm/Z07d4SDg4Po3bu3EEIIpVIpdP0V//e//y2sra2FJEkiMjJSTJgwQbRr104AEA899JA4e/aspu2VK1eElZWVaNGihSgvL6/S140bN4SNjY1o1qyZKC0trTLW++3fv1+4uLgIAKJTp05i7NixIiIiQkiSJOzt7cWuXbtqfR8qxcXFVZnDe8cNQAAQN2/e1Go/ffp0IUmSCA8PFxMmTBDh4eEiLy9PCCHEwoULBQBhZ2cnevfuLUaPHq2p5eGHHxZ37tzR1B0XFyciIyMFANG1a1cRFxcn4uLixIYNGzTjePfdd4UkScLa2lpERESIsWPHisDAQAFAtGzZUly9elVnTcYYY6XK3/Vnn31WWFlZiW7duonRo0cLPz8/ze96UVGR1jalpaVi7NixAoBQKpViwIABYvz48SIiIkLY2dmJuLg4rfZff/21sLW11Yxh7NixIiQkRAAQ7u7u4tSpU3rPK5GxMOCQrNQn4CQkJAgA4qmnnqrS/vr16+LkyZOa16WlpaJz584CgHj33Xe1PvzT09OFu7u7cHR0FNevX68yNgBi1KhR4u7duwbVdm/ASUlJEQDEihUrNOs///xzAUATlnQFnEuXLgl7e3thY2MjvvnmG83ysrIy8cwzzwgAIjw8XGub/v37CwBi3759Vcb0/vvvCwDimWee0TnWe92+fVs0a9ZMKBQK8dVXX2mtS01NFba2tqJ58+ZCpVLp9X7UFHDWr18vAIjmzZtXaQ9AJCUlVdnmyy+/FABESEiIyMjI0CwvKSkRM2bMEADE4sWLtbap/J25P/wKIcTBgweFlZWV8Pf3F7/++qtmeXl5uXj55ZcFADF27FidNRlzjJW/646OjiItLU2zvLCwUDzyyCMCgIiPj9fa5pVXXhEARJcuXURmZqbWups3b4oDBw5oXl+8eFE4ODiIJk2aiL1792q1/eyzz3T+ThE1BAYckpX6BJzXX39dABDbtm2rdT/btm0TAMTEiRN1rl+7dq0AIN5+++0qY1MqleLvv//Wpxwt9wac0tJS0axZM9GuXTvN+ujoaGFraytu3bolhNAdcJYtWyYAiMcee6xK/8XFxcLHx0cAEAcPHtQsj4+PFwDEzJkzq2xTeQTjyJEjOsd6rzVr1ggA4oUXXtBZX2XASk5OruWdqKAr4GRnZ4tPP/1Uc5Ro5cqVVdoPHTpUZ39du3YVAMTvv/9eZd3du3dFs2bNhKurqygrK9MsryngxMTECADixx9/rLKuvLxchISECCsrK3Hjxg2TjrHyd33p0qVVtklOThYAtI7IqFQq4erqKiRJqjKvusyfP18rWN9v5MiRAoA4duxYrX0RGRPPwSH6r4cffhgAsGTJEnz77bcoLi6utm3l+SYjR47Uub5nz54AKq6yuV9oaCiaN29er7FaW1tjwoQJOH/+PI4cOYJr164hLS0NQ4cOhZubW7Xb7d+/HwAQGxtbZZ1SqcS4ceO02gHAmDFjYGdnh6+//lrrhN1Lly7hl19+Qbt27RAWFlbrmOvzntVkypQpmvNfvLy8MHXqVOTn5yMuLk7nuUMjRoyosiw7Oxu//vorOnTogPbt21dZb2dnh7CwMOTl5eGPP/6odUzl5eVIS0uDs7MzBgwYUGW9JEmIjIxEeXk5jh071iBjjI6OrrKsXbt2AICsrCzNsqNHjyIvLw+hoaEGzWtMTIzO9XWdV6L64mXiJCv63lxOCFGl/YABA7BgwQKsXbsWw4cPh62tLYKDgxEdHY1p06Zp3U+l8sTJ8ePHY/z48dXup/IE13u1aNFCrzHWZvLkyVi7di02bdoEf39/lJWV1Xr1VOVJxNXdG6Zy+b0nGzdp0gRDhw5FcnIyUlJSMHz4cABAYmIihBA6w5Iule9Z9+7da2yn6z2ryb33wbGzs4O/vz8effRRBAcH62yv6/3/66+/AABnz56t9XcoJydHZ8C4182bNzUn/NrY1PyfWX1/R+o7Rl9f3yrtKu/Xc+/tBC5fvgyg4ko4fVTOa7NmzWodE1FDYsAhWbG3twcAFBYW1tiuqKgIAODo6Ki1/J133sHMmTPxn//8B2lpafj5559x+PBhvPHGG/jyyy81Rx/KysoAAI8++ii8vLyq3Y+uS6Xt7Oz0rqcmDz/8MDp06ICkpCR4e3vD1dUVQ4cO1Wvb2j4g718fGxuL5ORkJCYmagUcALVesVWp8j0bN24cHBwcqm1XWwC63/33wamNrve/cmze3t46j3Tcy93dvdZ9VPbn7OyM0aNH19jW39+/QcZo6J2l9W1fVlYGSZLw+OOP19iuU6dOBu2fqL4YcEhW/Pz8AFT8azE/Px8uLi462128eBGA7n/Vtm/fHs899xyee+45FBcX4/3338fixYsxc+ZMTcCp3G7WrFk6v05oKLGxsVi6dCmuX7+O6dOnQ6lU1tjex8cH586dQ0ZGhubriXtVHiXw9vbWWj506FC4urpix44duHPnDv766y+cPHkS3bt31xw9qY2vry/OnTuHpUuXIigoSM8KG0blfDZr1swoj+/w8PCAUqmEQqEw2uNAjD3G6lT+Hfrzzz/1au/r64sLFy7gvffeq/bvG5E58BwckhVvb2/NB+63336rs83PP/+MW7duwcnJSXO/lOrY2dlh0aJF8Pb2RnZ2NrKzswEAAwcOBABs377deIOvg9jYWHh4eMDd3b3Wf0EDQK9evQBUPOrhfiUlJfjqq6+02lWytbXF2LFjUVRUhO3bt2u21/frKaDxvGe6+Pr6on379vjtt9+QkZFR7/5sbGzQt29f3Lp1S+c9kerC2GOsTlhYGFxdXZGenq7z/KD7NeZ5pQcbAw7Jzvz58wEA//jHP/D7779rrcvKysKcOXMAVBx9ufeIx/bt23Ho0KEq/R0/fhzXr1+Hs7Oz5gTesWPHIjAwEBs3bsTrr79e5W65JSUl2Lp1K06ePGnU2u4XEBCAGzduICcnR3MyZ02mTZsGe3t7bN68Gd99951meXl5OZYsWYIrV64gPDwcPXr0qLJtZZjZtGkTkpKSYG1tXeP5R/ebOXMmPD098eqrryIhIUFzHlSlwsJCfP7559XeeM7Uli5dirKyMowZMwanTp2qsv7ChQv49NNP9e5vyZIlsLKyQlxcHA4cOFBl/dWrV/H++++bdYy62NraYsGCBRBCYNq0aZpzcirdunVL60Z/ixYtgr29PRYsWIBvvvmmSn+3bt3CBx98gLt379ZrXEQGM+9FXETGV15eLiZMmCAACIVCIfr27StiY2NFdHS0sLe3FwBEnz59qtyHpvJy1+bNm4thw4aJSZMmib59+wobGxsBQKxdu1ar/dmzZ0WLFi0EAOHt7S0GDRokxo0bJ3r06CFcXV2rXHJeeZn4/TdJ09e9l4nrQ58b/fXs2VNMnDhRtG/fXueN/u5VXl4ufH19NfdpGTx4cK1jvd+BAwdE06ZNNZfyDx06VHMzRUdHRwFAHD9+XK/6aroPTk3td+/eXW2b5557TgAQ1tbWIiwsTIwbN04MGjRIc3O+rl27arWv6TJxIYRYt26dsLa2FgBEUFCQGDNmjBg6dKjo3LmzsLa2Fk2aNDH5GCsvE7/3vjmVMjIyNH8f7qVWqzWXdyuVSjFw4EAxYcIE8cgjj+i80V9ycrLm71b79u3FyJEjRUxMjAgODtbcADA3N7famohMgQGHZKm8vFxs3rxZREdHCw8PD2FjYyPc3NxEr169xIcffihKSkqqbHP8+HGxaNEiER4eLry8vIRSqRT+/v5ixIgR1X7g3Lp1Syxfvlx07dpVODo6CgcHB9G6dWsxYsQIkZCQIAoKCjRtG0vAEUKIn3/+WQwfPly4u7sLhUIhWrRoIWbPnl3r/XmeffZZTcD5/PPPax2rLleuXBGLFi0SgYGBwt7eXjg5OYl27dqJ8ePHiy+//NIoN/qrqX1N4UEIIdLS0sSoUaM0NyX08vISoaGh4tlnn61yL5faAo4QQhw9elTExsYKPz8/oVAoRNOmTUVQUJCYO3eu2LNnj8nHWJeAI0TFzR8//fRT0bNnT+Hi4iLs7OxEy5YtRWxsrPjll1+qtD9//ryYOXOmaNWqlVAqlaJJkyaiQ4cOYsqUKeLbb7/VeSdsIlOShLjvODERERGRheM5OERERCQ7DDhEREQkOww4REREJDsMOERERCQ7DDhEREQkOww4REREJDsW/Syq8vJyXL16Fc7OzgY/SI6IiBrOrVu3EB4ejl27dul8wKgpPfbYY+jevTvmzZvXoPul6gkhUFBQAB8fH1hZmeZYi0XfB+fvv//WPBiOiIiILMvly5d1PvTYGCz6CI6zszOAijfI2E+xVavV2LlzJ6Kjo6FQKIzad2PA+iyf3GuUe32A/GusrK9Xr17o3Lkzvv76a3Tr1q1Ku0mTJqGkpASpqan466+/4Orqqvc+bt26pfUsuFu3biEyMhLr1q3Tehhs79698fjjj+PJJ5+sV033kvv8AaarMT8/H35+fprPcVOw6IBT+bWUi4uLSQKOg4MDXFxcZPmLy/osn9xrlHt9gPxrrKzvl19+gUKh0Dx5/F4ffvgh7ty5g2XLliE1NdXg/57f33bt2rVwcHDA448/DkdHR83yUaNGYfv27Vi4cGHdC7qP3OcPMH2Npjy9hCcZExGRSR04cABhYWFVlp85cwYvv/wyPv/8c6OdhxEfH48JEyZohRsA6NatGw4fPgyVSmWU/VDjx4BDREQmlZmZCR8fH61lKpUKEydOxJtvvokWLVoYZT+HDx/GqVOndH4N1bx5c6hUKly7ds0o+6LGjwGHiIhMqri4GHZ2dlrLXnjhBXTo0AGTJ0822n7i4+PRuXNnnef52NvbAwCKioqMtj9q3BhwiIjIpNzd3ZGbm6u1bNeuXfjqq69gY2MDGxsbDBgwAADg4eGBl156yeB9FBUVISkpqdqTiG/dugUA8PT0NLhvskwWfZIxERE1fsHBwdi8ebPWsuTkZNy9e1fz+siRI5g6dSr279+P1q1bG7yPLVu2QKVSVXtE6NSpU/D19YWHh4fBfZNlYsAhIiKTioqKwtKlS5Gbmws3NzcAqBJicnJyAAAdOnQw6DLxSvHx8Rg5ciTc3d11rt+/fz+io6MN7pcsF7+iIiIioxNCoKCkAAAQ0D4AYWFh2LJli8H9SJKEjRs31tjm/PnzOHDgAKZNm6ZzfXFxMbZt24bp06cbvH+yXAw4RERkNIUlhdhxbgem/GcKRiWNAgCMShoFx4GOWPnmShQUF+jcrm/fvhBCaB29yczMhI2NDSIjI2vcZ7t27SCEQFRUlM718fHx6N69O3r06FG3osgiMeAQEZFRpGelY9xX4/D8T8/jyJUjmnvbWFlZIatZFtTBaoz4eATSs9L16i8lJQUzZsxA27Zt6zUuhUKBdevW1asPsjw8B4eIiOotPSsdz6Q8g5yiHPg18YOttS0UqLjzrZudG5zsnOA70heXb1/GgpQFWDN4DUK9Q2vsc9asWUYZ24wZM4zSD1kWsx7BCQgIgCRJVX7mzp1rzmEREZEBCksKsXTXUuQU5aCVWyvYWtvqbGdrbYtWbq1wo+gGlu5aisKSwgYeKT1IzBpwjhw5gqysLM1PamoqAGDcuHHmHBYRERkgLSMNmXmZ8GviV+uzhSRJgl8TP2TmZWJXxq4GGiE9iMwacDw9PdGsWTPNz7fffovWrVujT58+5hwWERHpSQiBrWe3AkC1R27uZ2ttCwkSks8mQwhhyuHRA6zRnINTUlKCL774AgsXLqz2XwAqlUrrQWn5+fkAKp52qlarjTqeyv6M3W9jwfosn9xrlHt9gDxqLCgpQMbNDDzk8JDmnJtKla/vXw4AXg5eyLiZgbyiPDjZOjXIWI1NDvNXG1PV2BDvmSQaSXzesmULJk2ahEuXLlV5KFul5cuXY8WKFVWWJyYmwsHBwdRDJCIiIiMoKirCpEmTcPv2bbi4uJhkH40m4AwaNAi2trb45ptvqm2j6wiOn58fcnJyjP4GqdVqpKamIioqCgpF1X99WDrWZ/nkXqPc6wPkUWNBSQFGJY2ClZUV3OzctNYpoMAkh0lILEqEGtr/Ys8tzkV5eTm2T9hu0UdwLH3+amOqGvPz8+Hh4WHSgNMovqL666+/8NNPP2Hr1q01tlMqlVAqlVWWKxQKk/1ymbLvxoD1WT651yj3+gDLrtHNxg0t3VviyJUjcLLTHVTU//3fvbKLshHePByuDq61npjc2Fny/OnL2DU2xPvVKG70l5CQAC8vLwwdOtTcQyEiIgNIkoTRHUZDQKCkrESvbUrKSiAgMKbDGIsPN9R4mT3glJeXIyEhAXFxcbCxaRQHlIiIyAADWg5AgGsALt++XOtVUUIIXL59GQGuAejfsn8DjZAeRGYPOD/99BMuXbqEqVOnmnsoRERUB462jljZfyU8HTxxMfditUdySspKcDH3IjwdPLGq/yo42jo28EjpQWL2QybR0dG8DwIRkYUL9Q7FmsFrsHTXUmTmZUKCBC8HL8Ch4oTi7KJsCAgEuAZgVf9VCPEOMfeQSebMHnCIiEgeQr1D8dW4r7ArYxeSzyYj42YGgIpTEcKbh2NMhzHo37I/j9xQg2DAISIio3G0dcTw9sMxrN0w5BXlYd9P+7B9wnZZXC1FlsXs5+AQEZH8SJKkub+Nk60Tww01OAYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdBhwiIiKSHQYcIiIikh0GHCIiIpIdswecK1euYPLkyXB3d4eDgwOCg4Nx7Ngxcw+LiIiILJiNOXeem5uLyMhI9OvXDz/88AO8vLxw4cIFuLq6mnNYREREZOHMGnBef/11+Pn5ISEhQbMsICDAfAMiIiIiWTBrwNmxYwcGDRqEcePGYe/evWjevDnmzJmD6dOn62yvUqmgUqk0r/Pz8wEAarUaarXaqGOr7M/Y/TYWrM/yyb1GudcHyL9G1mf5TFVjQ7xnkhBCmHwv1bCzswMALFy4EOPGjcPhw4fxzDPP4KOPPsLjjz9epf3y5cuxYsWKKssTExPh4OBg8vESERFR/RUVFWHSpEm4ffs2XFxcTLIPswYcW1tbhIWF4ZdfftEse/rpp3HkyBEcPHiwSntdR3D8/PyQk5Nj9DdIrVYjNTUVUVFRUCgURu27MWB9lk/uNcq9PkD+NbI+y2eqGvPz8+Hh4WHSgGPWr6i8vb3RsWNHrWUdOnRAcnKyzvZKpRJKpbLKcoVCYbJfLlP23RiwPssn9xrlXh8g/xpZn+Uzdo0N8X6Z9TLxyMhInDt3TmvZ+fPn4e/vb6YRERERkRyYNeAsWLAAhw4dwquvvoo///wTiYmJ+PjjjzF37lxzDouIiIgsnFkDTnh4OLZt24bNmzejc+fOeOWVV7B27VrExsaac1hERERk4cx6Dg4ADBs2DMOGDTP3MIiIiEhGzP6oBiIiIiJjY8AhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAh2bt58ya8vLyQmZnZ4PseO3Ys3nnnnQbfLxHRg44Bh2Rv9erVGD58OAICAgAAv/76KyZOnAg/Pz/Y29ujQ4cOePfddw3ud+PGjZAkqcpPcXGxps2yZcuwatUq5OfnG6scIiLSg425B0BkSnfv3kV8fDy+//57zbJjx47B09MTX3zxBfz8/PDLL79gxowZsLa2xrx58wzq38XFBefOndNaZmdnp/lzUFAQAgICsGnTJsyePbt+xRARkd4MDjgLFy7UuVySJNjZ2aFNmzaIiYlB06ZN6z04ovpKSUmBjY0NIiIiNMumTp2q1aZVq1Y4ePAgtm7danDAkSQJzZo1q7HNiBEjsHnzZgYcIqIGZHDAOX78ONLT01FWVob27dtDCIE//vgD1tbWCAwMxAcffIBFixbhwIED6NixoynGTKS3AwcOICwsrNZ2t2/frlMov3PnDvz9/VFWVobg4GC88sorCAkJ0WrTrVs3rF69GiqVCkql0uB9EBGR4Qw+BycmJgYDBw7E1atXcezYMaSnp+PKlSuIiorCxIkTceXKFfTu3RsLFiwwxXiJDJKZmQkfH58a2xw8eBBbtmzBzJkzDeo7MDAQGzduxI4dO7B582bY2dkhMjISf/zxh1a75s2bQ6VS4dq1awaPn4iI6sbgIzhvvvkmUlNT4eLiolnm4uKC5cuXIzo6GvPnz8eyZcsQHR1t1IES1UVxcbHWOTH3O336NGJiYrBs2TJERUUZ1HePHj3Qo0cPzevIyEiEhoZi3bp1eO+99zTL7e3tAQBFRUUGjp6IiOrK4CM4t2/fRnZ2dpXlN27c0Fwp4urqipKSkvqPjqie3N3dkZubq3PdmTNn0L9/f0yfPh1Lly6t976srKwQHh5e5QjOrVu3AACenp713gcREemnTl9RTZ06Fdu2bcPff/+NK1euYNu2bZg2bRpGjhwJADh8+DDatWtn7LESGSw4OBhnzpypsvz06dPo168f4uLisGrVKqPsSwiBEydOwNvbW2v5qVOn4OvrCw8PD6Psh4iIamfwV1QfffQRFixYgAkTJqC0tLSiExsbxMXFYc2aNQAqzk345JNPjDtSIj0JIVBQUgAAiOwbiaVLlyI3Nxdubm4A/hduoqOjsXDhQs25MdbW1gYdZVmxYgV69OiBtm3bIj8/H++99x5OnDiB999/X6vd/v37+ZUtEVEDM/gIjpOTEzZs2ICbN29qrqi6efMmPv74Yzg6OgKo+FdzcHCwscdKVKPCkkLsOLcDU/4zBaOSRgEA/nnqn3Br5YZ/vvdPFJYUAgC++uor3LhxA5s2bYK3t7fmJzw8XKs/SZKwcePGaveXl5eHGTNmoEOHDoiOjsaVK1ewb98+dOvWTdOmuLgY27Ztw/Tp041fMBERVavON/pzcnJCUFCQMcdCVGfpWelYumspMvMyIUGCl4MXgIrzYuwG2CF+fTwyWmVg1cBVWL58OZYvX15jf5mZmbCxsUFkZGS1bdasWaM5almd+Ph4dO/eXetkZCIiMj2DA05hYSFee+01pKWlITs7G+Xl5VrrL168aLTBEekjPSsdz6Q8g5yiHPg18YOttS0UUAAA3Ozc0LVXV/xR8Af+/OtPLEhZgDWD1yDUO7TGPlNSUjBjxgy0bdu2XmNTKBRYt25dvfogIiLDGRxwnnzySezduxePPfYYvL29IUlSnXe+fPlyrFixQmvZQw89xPuFkN4KSwqxdNdS5BTloJVbq2p/H9sOaQshBC7mXsTSXUvx1biv4GjrWG2/s2bNMsr4ZsyYYZR+iIjIMAYHnB9++AHfffddjYfuDdGpUyf89NNPmtfW1tZG6ZceDGkZacjMy4RfE79aw7YkSfBr4ofMvEzsytiF4e2HN9AoiYiooRkccNzc3Iz6nCkbG5tan+VDpIsQAlvPbgUA2Frb6rWNrbUtJEhIPpuMYe2G1esIJBERNV4GB5xXXnkFy5Ytw2effQYHB4d6D+CPP/6Aj48PlEolunfvjldffRWtWrXS2ValUkGlUmleV95YUK1WQ61W13ss96rsz9j9NhZyqK+gpAAZNzPwkMNDmnNuKlW+vn85AHg5eCHjZgbyivLgZOvUIGM1BTnMYU3kXh8g/xpZn+UzVY0N8Z5JQghhyAYhISG4cOEChBAICAiAQqH9AZKenq53Xz/88AOKiorQrl07XL9+HStXrsTvv/+O06dPw93dvUp7XefsAEBiYqJRwhYRERGZXlFRESZNmoTbt29rPfrJmAwOOLoCxr1eeumlOg+msLAQrVu3xnPPPYeFCxdWWa/rCI6fnx9ycnKM/gap1WqkpqYiKiqqSoiTAznUV1BSgFFJo2BlZQU3OzetdQooMMlhEhKLEqGG9r8UcotzUV5eju0Ttlv8ERxLn8OayL0+QP41sj7LZ6oa8/Pz4eHhYdKAY/BXVPUJMLVxdHREly5dqjzLp5JSqYRSqayyXKFQmOyXy5R9NwaWXJ+bjRtaurfEkStH4GSnO6io//u/e2UXZSO8eThcHVxlcQ6OJc+hPuReHyD/Glmf5TN2jQ3xfhl8J2NTUqlUOHv2bJVn+RDpIkkSRncYDQGBkjL9Hu5aUlYCAYExHcbIItwQEZFueh3Badq0Kc6fPw8PDw+4ubnV+MFQ+eRkfSxevBjDhw9HixYtkJ2djZUrVyI/Px9xcXF690EPtgEtByDANQCZeZk13gcHqLjq6vLtywhwDUD/lv0bcJRERNTQ9Ao4a9asgbOzs+bPxvqX799//42JEyciJycHnp6e6NGjBw4dOgR/f3+j9E/y52jriJX9V2JBygJczL2ouZPx/UrKSnD59mV4OnhiVf9VNd7kj4iILJ9eAefeIypPPPGE0XaelJRktL7owRXqHYo1g9dUfRaVQ8UJxdlF2RAQCHANwKr+qxDiHWLuIRMRkYkZfJKxtbU1srKy4OXlpbX85s2b8PLyQllZmdEGR6SvUO9QfDXuK+zK2IXks8nIuJkBACgvL0d483CM6TAG/Vv255EbIqIHhMEBp7qrylUqFWxt9bubLJEpONo6Ynj74RjWbhjyivKw76d92D5hu2yuliIiIv3pHXDee+89ABVXrnzyySdwcvrfZbllZWXYt28fAgMDjT9CIgNJkqS5v42TrRPDDRHRA0jvgLNmzRoAFUdw1q9fr/VQTFtbWwQEBGD9+vXGHyERERGRgfQOOBkZFec09OvXD1u3boWbm1stWxARERGZh8Hn4OzevdsU4yAiIiIyGoMDDlBx/5odO3bg0qVLKCnRvoPsO++8Y5SBEREREdWVwQEnLS0NI0aMQMuWLXHu3Dl07twZmZmZEEIgNDTUFGMkIiIiMojBz6J64YUXsGjRIpw6dQp2dnZITk7G5cuX0adPH4wbN84UYyQiIiIyiMEB5+zZs5o7G9vY2ODu3btwcnLCyy+/jNdff93oAyQiIiIylMEBx9HRESqVCgDg4+ODCxcuaNbl5OQYb2REREREdWTwOTg9evTAzz//jI4dO2Lo0KFYtGgRTp48ia1bt6JHjx6mGCMRERGRQQwOOO+88w7u3LkDAFi+fDnu3LmDL7/8Em3atNHcDJCIiIjInAwKOGVlZbh8+TKCgoIAAA4ODvjggw9MMjAiIiKiujLoHBxra2sMGjQIeXl5JhoOERERUf0ZfJJxly5dcPHiRVOMhYiIiMgoDA44q1atwuLFi/Htt98iKysL+fn5Wj9ERERE5mbwScaDBw8GAIwYMQKSJGmWCyEgSRLKysqMNzoiIiKiOuDDNomIiEh2DA44ffr0McU4iIiIiIzG4HNwiIiIiBo7BhwiIiKSHQYcIiIikh0GHCIiIpKdegeckpISzbOpiIiIiBoDgwJOQkICnnrqKWzatAkA8MILL8DZ2RlNmjRBVFQUbt68aZJBEhERERlC74CzatUqzJ07F2fPnsXTTz+N2bNnY+PGjXj55Zfx2muv4ffff8fSpUtNOVYiIiIiveh9H5yNGzciPj4eEydOxNGjR9G9e3d8+eWXGDt2LACgc+fOmDVrlskGSkRERKQvvY/gXLp0CT179gQAhIWFwcbGBl26dNGsDwoKQlZWlvFHSERERGQgvQOOWq2GUqnUvLa1tYVCodC8trGx4XOoiIiIqFEw6FENZ86cwbVr1wBUPFzz999/11xBlZOTY/zREREREdWBQQFnwIABEEJoXg8bNgwAIEmS5mniREREROamd8DJyMgw5TiIiIiIjEbvgOPv72/KcRAREREZDR/VQERERLLDgENERESyw4BDREREssOAQ0RERLJTp4BTWlqKn376CR999BEKCgoAAFevXuVTxYmIiKhRMOg+OADw119/YfDgwbh06RJUKhWioqLg7OyMN954A8XFxVi/fr0pxklERESkN4OP4MyfPx9hYWHIzc2Fvb29ZvmoUaOQlpZm1MERERER1YXBR3AOHDiAn3/+Gba2tlrL/f39ceXKFaMNjIiIiKiuDD6CU15ervOhmn///TecnZ2NMigiIiKi+jA44ERFRWHt2rWa15Ik4c6dO3jppZcwZMgQY46NiAgAcPPmTXh5eSEzM7PB9z127Fi88847Db5fIqofgwPOmjVrsHfvXnTs2BHFxcWYNGkSAgICcOXKFbz++uumGCMRPeBWr16N4cOHIyAgAEBF4Bk8eDB8fHygVCrh5+eHefPmIT8/v877SEpKgiRJGDlypNbyZcuWYdWqVfXqm4gansHn4Pj4+ODEiRPYvHkz0tPTUV5ejmnTpiE2NlbrpGMiImO4e/cu4uPj8f3332uWWVlZISYmBitXroSnpyf+/PNPzJ07F7du3UJiYqLB+/jrr7+wePFi9OrVq8q6oKAgBAQEYNOmTZg9e3a9aiGihmNwwAEAe3t7TJ06FVOnTjX2eIiItKSkpMDGxgYRERGaZW5ublphw9/fH3PmzMGbb75pcP9lZWWIjY3FihUrsH//fuTl5VVpM2LECGzevJkBh8iC6BVwduzYgUcffRQKhQI7duyose2IESOMMjAiIqDiys2wsLAa21y9ehVbt25Fnz59DO7/5ZdfhqenJ6ZNm4b9+/frbNOtWzesXr0aKpUKSqXS4H0QUcPTK+CMHDkS165dg5eXV5Xvp+8lSZLOK6yIiOoqMzMTPj4+OtdNnDgR//nPf3D37l0MHz4cn3zyiUF9//zzz4iPj8eJEydqbNe8eXOoVCpcu3YN/v7+Bu2DiMxDr5OMy8vL4eXlpflzdT8MN0RkbMXFxbCzs9O5bs2aNUhPT8f27dtx4cIFLFy4UO9+CwoKMHnyZGzYsAEeHh41tq08v7CoqEj/gRORWRl8Dk5mZqbmSgYiIlNzd3dHbm6uznXNmjVDs2bNEBgYCHd3d/Tq1QsvvvgivL29a+33woULyMzMxPDhwzXLysvLAQA2NjY4d+4cWrduDQC4desWAMDT07O+5RBRAzH4MvFWrVqhZ8+e+OijjzR/6YmITCU4OBhnzpyptZ0QAgCgUqn06jcwMBAnT57EiRMnND8jRoxAv379cOLECfj5+Wnanjp1Cr6+vrUe6SGixsPggHP06FFERERg5cqV8PHxQUxMDL766iu9/6NCRGSIqKgonD59Wusozvfff4+EhAScOnUKmZmZ+P777zF79mxERkbqfYTZzs4OnTt31vpxdXWFs7MzOnfurPU4mv379yM6OtrYpRGRCRkccEJDQ/Hmm2/i0qVL+OGHH+Dl5YWZM2fCy8uLl40TkdF16dIFYWFh2LJli2aZvb09NmzYgJ49e6JDhw545plnMGzYMHz77bda20qShI0bN9Zr/8XFxdi2bRumT59er36IqGHV6T44QMV/OPr164d+/fph9uzZmDZtGj777DN8+umnxhwfERFefPFFLF68GNOnT4eVlRX69euHX375pcZtMjMzYWNjg8jISL33oysMxcfHo3v37ujRo4ehwyYiMzL4CE6ly5cv44033kBwcDDCw8Ph6OiIf/3rX8YcGxERAGDIkCGYOXMmrly5ovc2KSkpmDFjBtq2bVuvfSsUCqxbt65efRBRwzP4CM7HH3+MTZs24eeff0b79u0RGxuL7du31/vKqtWrV2PJkiWYP3++1sM8iYgAYP78+Qa1nzVrllH2O2PGDKP0Q0QNy+CA88orr2DChAl49913ERwcbJRBHDlyBB9//DGCgoKM0h8RERE92AwOOJcuXYIkSUYbwJ07dxAbG4sNGzZg5cqVRuuXiIiIHlwGBxxJkpCXl4f4+HicPXsWkiShQ4cOmDZtGpo0aWLwAObOnYuhQ4di4MCBDDhERERkFAYHnKNHj2LQoEGwt7dHt27dIITAmjVr8Oqrr2Lnzp0IDQ3Vu6+kpCSkp6fjyJEjerVXqVRa99vJz88HAKjVaqjVasMKqUVlf8but7FgfZZP7jXKvT5A/jWyPstnqhob4j2TROXtP/XUq1cvtGnTBhs2bICNTUU+Ki0txZNPPomLFy9i3759evVz+fJlhIWFYefOnejatSsAoG/fvggODq72JOPly5djxYoVVZYnJibCwcHBkDKIiIjITIqKijBp0iTcvn0bLi4uJtmHwQHH3t4ex48fR2BgoNbyM2fOICwsTO+H0W3fvh2jRo2CtbW1ZllZWRkkSYKVlRVUKpXWOkD3ERw/Pz/k5OQY/Q1Sq9VITU1FVFQUFAqFUftuDFif5ZN7jXKvD5B/jazP8pmqxvz8fHh4eJg04Bj8FZWLiwsuXbpUJeBcvnwZzs7OevczYMAAnDx5UmvZlClTEBgYiH/84x9Vwg0AKJVKKJXKKssVCoXJfrlM2XdjwPosn9xrlHt9gPxrZH2Wz9g1NsT7ZXDAGT9+PKZNm4a33noLjzzyCCRJwoEDB/Dss89i4sSJevdT+byXezk6OsLd3b3KciIiIiJDGBxw3nrrLUiShMcffxylpaUAKpLY7Nmz8dprrxl9gERERESGMjjg2Nra4t1338Xq1atx4cIFCCHQpk0bo5zku2fPnnr3QURERFTnh206ODigS5cuxhwLERERkVHoHXCmTp2qVzs+TZyIiIjMTe+As3HjRvj7+yMkJAQGXllORERE1KD0DjizZs1CUlISLl68iKlTp2Ly5Mlo2rSpKcdGREREVCdW+jb84IMPkJWVhX/84x/45ptv4Ofnh//7v//Djz/+yCM6RERE1KjoHXCAihvtTZw4EampqThz5gw6deqEOXPmwN/fH3fu3DHVGImIiIgMYlDAuZckSZAkCUIIlJeXG3NMRERERPViUMBRqVTYvHkzoqKi0L59e5w8eRL/+te/cOnSJTg5OZlqjEREREQG0fsk4zlz5iApKQktWrTAlClTkJSUBHd3d1OOjYiIiKhO9A4469evR4sWLdCyZUvs3bsXe/fu1dlu69atRhscERERUV3oHXAef/xxSJJkyrEQERERGYVBN/ojIiIisgR1voqKiIiIqLFiwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2WHAISIiItlhwCGiRu/mzZvw8vJCZmZmg+977NixeOeddxp8v0RUPww4RNTorV69GsOHD0dAQACAisAzePBg+Pj4QKlUws/PD/PmzUN+fr5B/W7duhVhYWFwdXWFo6MjgoOD8e9//1urzbJly7Bq1SqD+yYi82LAIaJG7e7du4iPj8eTTz6pWWZlZYWYmBjs2LED58+fx8aNG/HTTz9h1qxZBvXdtGlT/POf/8TBgwfx22+/YcqUKZgyZQp+/PFHTZugoCAEBARg06ZNRquJiEyPAYeIGrWUlBTY2NggIiJCs8zNzQ2zZ89GWFgY/P39MWDAAMyZMwf79+83qO++ffti1KhR6NChA1q3bo358+cjKCgIBw4c0Go3YsQIbN682Sj1EFHDYMAhokbtwIEDCAsLq7HN1atXsXXrVvTp06fO+xFCIC0tDefOnUPv3r211nXr1g2HDx+GSqWqc/9E1LAYcIioUcvMzISPj4/OdRMnToSDgwOaN28OFxcXfPLJJwb3f/v2bTg5OcHW1hZDhw7FunXrEBUVpdWmefPmUKlUuHbtWp1qIKKGx4BDRI1acXEx7OzsdK5bs2YN0tPTsX37dly4cAELFy40uH9nZ2ecOHECR44cwapVq7Bw4ULs2bNHq429vT0AoKioyOD+icg8bMw9ACKimri7uyM3N1fnumbNmqFZs2YIDAyEu7s7evXqhRdffBHe3t56929lZYU2bdoAAIKDg3H27FmsXr0affv21bS5desWAMDT07PuhRBRg+IRHCJq1IKDg3HmzJla2wkhAKDe58kIIar0cerUKfj6+sLDw6NefRNRw+ERHCJq1KKiorB06VLk5ubCzc0NAPD999/j+vXrCA8Ph5OTE86cOYPnnnsOkZGRmnvl6GP16tUICwtD69atUVJSgu+//x6ff/45PvzwQ612+/fvR3R0tDHLIiIT4xEcImp0hAAKCir+HBDQBWFhYdiyZYtmvb29PTZs2ICePXuiQ4cOeOaZZzBs2DB8++23Wv1IkoSNGzdWu5/CwkLMmTMHnTp1wiOPPIKvv/4aX3zxhdY9d4qLi7Ft2zZMnz7dqDUSkWnxCA4RNRqFhUBaGrB1K5CRASxcCIwaBTg6voiVKxdj0qTpcHa2Qr9+/fDLL7/U2FdmZiZsbGwQGRlZbZuVK1di5cqVNfYTHx+P7t27o0ePHnWqiYjMg0dwiKhRSE8Hxo0Dnn8eOHIEsPrvf52srICsrCFQq2dixIgrSE/Xr7+UlBTMmDEDbdu2rde4FAoF1q1bV68+iKjhmTXgfPjhhwgKCoKLiwtcXFwQERGBH374wZxDIiIzSE8HnnkGyMwE/PyAVq2A/55uAze3itddu87H9et+WLAAeoWcWbNm4f3336/32GbMmIH27dvXux8ialhmDTi+vr547bXXcPToURw9ehT9+/dHTEwMTp8+bc5hEVEDKiwEli4FcnIqgoytre52trYV62/cqGhfWNiw4yQiy2LWgDN8+HAMGTIE7dq1Q7t27bBq1So4OTnh0KFD5hwWETWgtLT/HbmRpJrbSlJFu8xMYNeuhhgdEVmqRnMOTllZGZKSklBYWKj1UD0iki8hKk4oBqo/cnM/W9uKoJOcXLE9EZEuZr+K6uTJk4iIiEBxcTGcnJywbds2dOzYUWdblUqldQOu/Px8AIBarYZarTbquCr7M3a/jQXrs3xyqLGgoOJqqYceAhQK7XUKhVrr/+/l5VWxXV4e4OTUAAM1ETnMYU1Yn+UzVY0N8Z5JQpj330AlJSW4dOkS8vLykJycjE8++QR79+7VGXKWL1+OFStWVFmemJgIBweHhhguERER1VNRUREmTZqE27dvw8XFxST7MHvAud/AgQPRunVrfPTRR1XW6TqC4+fnh5ycHKO/QWq1GqmpqYiKioLi/n9aygDrs3xyqLGgoOI+N1ZW/7tqqpJCocakSalITIyCWq1dX24uUF4ObN9u+UdwLH0Oa8L6LJ+paszPz4eHh4dJA47Zv6K6n67nwFRSKpVQKpVVlisUCpP9cpmy78aA9Vk+S67RzQ1o2bLivjfVBRW1WlEl4GRnA+HhgKtr7ScmWwJLnkN9sD7LZ+waG+L9MmvAWbJkCR599FH4+fmhoKAASUlJ2LNnD1JSUsw5LCJqIJIEjB4NHD4MlJTod6JxSUnFycVjxsgj3BCRaZg14Fy/fh2PPfYYsrKy0KRJEwQFBSElJQVRUVHmHBYRNaABA4CAgIpLv1u1qjm0CAFcvlzRvn//BhogEVkkswac+Ph4c+6eiBoBR0dg5UpgwQLg4sWK+9zoOpJTUlIRbjw9gVWrKrYjIqpOo7kPDhE9uEJDgTVrKo7MXL5cEXRycyvW5eZWvK48crN2LRASYsbBEpFFaHQnGRPRgyk0FPjqq4o7FCcnV9znBqi4Wio8vOKcm/79eeSGiPTDgENEjYajIzB8ODBsWMVN/Pbtq7gUXC5XSxFRw+FXVETU6EjS/y4bd3JiuCEiwzHgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDg6OHmzZvw8vJCZmZmg+977NixeOeddxp8v0RERPeytM9CBhw9rF69GsOHD0dAQIBm2fz58/Hwww9DqVQiODi4Tv1u2LABvXr1gpubG9zc3DBw4EAcPnxYq82yZcuwatUq5Ofn16MCIiKi+tH1WShJUpWf9evXG9SvqT4LGXBqcffuXcTHx+PJJ5/UWi6EwNSpUzF+/Pg6971nzx5MnDgRu3fvxsGDB9GiRQtER0fjypUrmjZBQUEICAjApk2b6rwfIiKi+qjusxAAEhISkJWVpfmJi4szqG9TfRYy4NQiJSUFNjY2iIiI0Fr+3nvvYe7cuWjVqlWd+960aRPmzJmD4OBgBAYGYsOGDSgvL0daWppWuxEjRmDz5s113g8REVF9VPdZCACurq5o1qyZ5sfe3t6gvk31WciAU4sDBw4gLCysQfZVVFQEtVqNpk2bai3v1q0bDh8+DJVK1SDjICIiuldNn4Xz5s2Dh4cHwsPDsX79epSXl9drX8b6LLSp1ygeAJmZmfDx8WmQfT3//PNo3rw5Bg4cqLW8efPmUKlUuHbtGvz9/RtkLERERJWq+yx85ZVXMGDAANjb2yMtLQ2LFi1CTk4Oli5dWud9GeuzkAGnFsXFxbCzszP5ft544w1s3rwZe/bsqbK/ysN9RUVFJh8HERHR/ar7LLw3yFRecPPyyy/XOeAY87OQX1HVwt3dHbm5uSbdx1tvvYVXX30VO3fuRFBQUJX1t27dAgB4enqadBxERES66PtZ2KNHD+Tn5+P69esG78PYn4UMOLUIDg7GmTNnTNb/m2++iVdeeQUpKSnVfr956tQp+Pr6wsPDw2TjICIiqo6+n4XHjx+HnZ0dXF1dDerfFJ+F/IqqFlFRUVi6dClyc3Ph5uamWf7nn3/izp07uHbtGu7evYsTJ04AADp27AhbW1u9+n7jjTfw4osvIjExEQEBAbh27RoAwMnJCU5OTpp2+/fvR3R0tPGKIiIiMoCuz8JvvvkG165dQ0REBOzt7bF7927885//xIwZM6BUKvXu21SfhTyCo4MQQEFBxZ8DArogLCwMW7Zs0Wrz5JNPIiQkBB999BHOnz+PkJAQhISE4OrVq5o2kiRh48aN1e7ngw8+QElJCcaOHQtvb2/Nz1tvvaVpU1xcjG3btmH69OlGrZGIiKgmtX0WKhQKfPDBB4iIiEBQUBDeffddvPzyy3j77be1+jHXZyGP4NyjsBBISwO2bgUyMoCFC4FRowBHxxexcuViTJo0Hc7OFZlwz549NfaVmZkJGxsbREZG1timNvHx8ejevTt69OhhSClERER1ou9n4eDBgzF48OAa+zLnZyGP4PxXejowbhzw/PPAkSOA1X/fGSsrICtrCNTqmRgx4grS0/XrLyUlBTNmzEDbtm3rNS6FQoF169bVqw8iIiJ9yOmzkEdwUDGhzzwD5OQAfn6ArS2gUFSsc3MDnJwAX9/5uHwZWLAAWLMGCA2tuc9Zs2YZZWwzZswwSj9EREQ1kdtnoVmP4KxevRrh4eFwdnaGl5cXRo4ciXPnzjXoGAoLgaVLKya0VauKCdXF1rZi/Y0bFe0LCxt0mERERCYjx89CswacvXv3Yu7cuTh06BBSU1NRWlqK6OhoFDbgO5aWBmRmVqRVSaq5rSRVtMvMBHbtaojRERERmZ4cPwvN+hVVSkqK1uuEhAR4eXnh2LFj6N27t8n3L0TFSVRA9Wn1fra2FZObnAwMG1b7LwIREVFjJtfPwkZ1Ds7t27cBoMoDtiqpVCqth2zl5+cDANRqNdRqtcH7KyioOEP8oYf+9z1jJYVCrfX/9/LyqtguL6/iO0lLVPl+1eV9swRyrw+Qf41yrw+Qf42szzKY47OwId4zSQghTL4XPQghEBMTg9zcXOzfv19nm+XLl2PFihVVlicmJsLBwcHUQyQiIiIjKCoqwqRJk3D79m24uLiYZB+NJuDMnTsX3333HQ4cOABfX1+dbXQdwfHz80NOTk6d3qCCgopr+62sKs4Qv5dCocakSalITIyCWq0daXNzgfJyYPt2yz6Ck5qaiqioKCjuj+wyIPf6APnXKPf6APnXyPosgzk+C/Pz8+Hh4WHSgNMovqJ66qmnsGPHDuzbt6/acAMASqVS5+2fFQpFnX653NyAli0rrvWvbnLUakWVSc3OBsLDAVfXxvm9oyHq+t5ZCrnXB8i/RrnXB8i/RtbXuJnjs7Ah3i+zXkUlhMC8efOwdetW7Nq1Cy1btmzQ/UsSMHp0xQlWJSX6bVNSUtF+zBjLDzdERERy/Sw0a8CZO3cuvvjiCyQmJsLZ2RnXrl3TPLyyoQwYAAQEAJcvV0xWTYSoaBcQAPTv3xCjIyIiMj05fhaaNeB8+OGHuH37Nvr27av1gK0vv/yywcbg6AisXAl4egIXL1afXktKKtZ7egKrVlVsR0REJAdy/Cw06zk4jeT8ZoSGVtxyeunSihsXSVLF5W9AxUlU2dkViTUgoGJCQ0LMOVoiIiLjk9tnYaM4ybgxCA0Fvvqq4q6MyckV1/YDFWeIh4dXfM/Yv3/jTqtERET1IafPQgacezg6AsOHV9yVMS8P2Lev4vI3OVwtRUREpA+5fBaa9RycxkqS/nepnJOTZU0oERGRMVj6ZyEDDhEREckOAw4RERHJDgMOERERyQ4DDhEREckOAw4RERHJDgMOERERyY5F3wen8k7I+fn5Ru9brVajqKgI+fn5Fv2U2OqwPssn9xrlXh8g/xpZn+UzVY2Vn9umfKKBRQecgoICAICfn5+ZR0JERESGKigoQJMmTUzStyQaywOh6qC8vBxXr16Fs7MzJCPfgSg/Px9+fn64fPkyXFxcjNp3Y8D6LJ/ca5R7fYD8a2R9ls9UNQohUFBQAB8fH1hZmeZsGYs+gmNlZQVfX1+T7sPFxUW2v7gA65MDudco9/oA+dfI+iyfKWo01ZGbSjzJmIiIiGSHAYeIiIhkhwGnGkqlEi+99BKUSqW5h2ISrM/yyb1GudcHyL9G1mf5LLlGiz7JmIiIiEgXHsEhIiIi2WHAISIiItlhwCEiIiLZYcAhIiIi2XkgA86+ffswfPhw+Pj4QJIkbN++vdZt9u7di4cffhh2dnZo1aoV1q9fb/qB1oOhNe7ZsweSJFX5+f333xtmwAZYvXo1wsPD4ezsDC8vL4wcORLnzp2rdTtLmsO61GhJc/jhhx8iKChIc/OwiIgI/PDDDzVuY0nzBxheoyXNny6rV6+GJEl45plnamxnafNYSZ/6LG0Oly9fXmWszZo1q3EbS5q/BzLgFBYWomvXrvjXv/6lV/uMjAwMGTIEvXr1wvHjx7FkyRI8/fTTSE5ONvFI687QGiudO3cOWVlZmp+2bduaaIR1t3fvXsydOxeHDh1CamoqSktLER0djcLCwmq3sbQ5rEuNlSxhDn19ffHaa6/h6NGjOHr0KPr374+YmBicPn1aZ3tLmz/A8BorWcL83e/IkSP4+OOPERQUVGM7S5xHQP/6KlnSHHbq1ElrrCdPnqy2rcXNn3jAARDbtm2rsc1zzz0nAgMDtZbNnDlT9OjRw4QjMx59aty9e7cAIHJzcxtkTMaUnZ0tAIi9e/dW28bS51CfGi15DoUQws3NTXzyySc611n6/FWqqUZLnb+CggLRtm1bkZqaKvr06SPmz59fbVtLnEdD6rO0OXzppZdE165d9W5vafP3QB7BMdTBgwcRHR2ttWzQoEE4evQo1Gq1mUZlGiEhIfD29saAAQOwe/ducw9HL7dv3wYANG3atNo2lj6H+tRYydLmsKysDElJSSgsLERERITONpY+f/rUWMnS5m/u3LkYOnQoBg4cWGtbS5xHQ+qrZElz+Mcff8DHxwctW7bEhAkTcPHixWrbWtr8WfTDNhvKtWvX8NBDD2kte+ihh1BaWoqcnBx4e3ubaWTG4+3tjY8//hgPP/wwVCoV/v3vf2PAgAHYs2cPevfube7hVUsIgYULF6Jnz57o3Llzte0seQ71rdHS5vDkyZOIiIhAcXExnJycsG3bNnTs2FFnW0udP0NqtLT5A4CkpCSkp6fjyJEjerW3tHk0tD5Lm8Pu3bvj888/R7t27XD9+nWsXLkSjzzyCE6fPg13d/cq7S1t/hhw9CRJktZr8d8bQN+/3FK1b98e7du317yOiIjA5cuX8dZbbzXKv5iV5s2bh99++w0HDhyota2lzqG+NVraHLZv3x4nTpxAXl4ekpOTERcXh71791YbACxx/gyp0dLm7/Lly5g/fz527twJOzs7vbezlHmsS32WNoePPvqo5s9dunRBREQEWrdujc8++wwLFy7UuY2lzB/wgJ5kbKhmzZrh2rVrWsuys7NhY2OjM+XKRY8ePfDHH3+YexjVeuqpp7Bjxw7s3r0bvr6+Nba11Dk0pEZdGvMc2traok2bNggLC8Pq1avRtWtXvPvuuzrbWur8GVKjLo15/o4dO4bs7Gw8/PDDsLGxgY2NDfbu3Yv33nsPNjY2KCsrq7KNJc1jXerTpTHP4f0cHR3RpUuXasdrSfMH8AiOXiIiIvDNN99oLdu5cyfCwsKgUCjMNCrTO378eKM75AhU/IvhqaeewrZt27Bnzx60bNmy1m0sbQ7rUqMujXUOdRFCQKVS6VxnafNXnZpq1KUxz9+AAQOqXHEzZcoUBAYG4h//+Aesra2rbGNJ81iX+nRpzHN4P5VKhbNnz6JXr14611vS/AF4MK+iKigoEMePHxfHjx8XAMQ777wjjh8/Lv766y8hhBDPP/+8eOyxxzTtL168KBwcHMSCBQvEmTNnRHx8vFAoFOLrr782Vwm1MrTGNWvWiG3btonz58+LU6dOieeff14AEMnJyeYqoVqzZ88WTZo0EXv27BFZWVman6KiIk0bS5/DutRoSXP4wgsviH379omMjAzx22+/iSVLlggrKyuxc+dOIYTlz58QhtdoSfNXnfuvMpLDPN6rtvosbQ4XLVok9uzZIy5evCgOHTokhg0bJpydnUVmZqYQwvLn74EMOJWX8t3/ExcXJ4QQIi4uTvTp00drmz179oiQkBBha2srAgICxIcfftjwAzeAoTW+/vrronXr1sLOzk64ubmJnj17iu+++848g6+FrroAiISEBE0bS5/DutRoSXM4depU4e/vL2xtbYWnp6cYMGCA5oNfCMufPyEMr9GS5q869wcAOczjvWqrz9LmcPz48cLb21soFArh4+MjRo8eLU6fPq1Zb+nzJwnx3zOEiIiIiGSCJxkTERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEJHRSJKE7du3m3sY9bZ8+XIEBwebexhEVA8MOEQy9cQTT0CSJMyaNavKujlz5kCSJDzxxBNG3WdWVpbWE4rrIzo6GtbW1jh06JBR+quOrlC2ePFipKWlmXS/RGRaDDhEMubn54ekpCTcvXtXs6y4uBibN29GixYtjL6/Zs2aQalU1rufS5cu4eDBg5g3bx7i4+MN3r6srAzl5eV13r+Tk1OjfDoyEemPAYdIxkJDQ9GiRQts3bpVs2zr1q3w8/NDSEiIVluVSoWnn34aXl5esLOzQ8+ePXHkyBEAQHl5OXx9fbF+/XqtbdLT0yFJEi5evAig6tGQK1euYPz48XBzc4O7uztiYmKQmZlZ67gTEhIwbNgwzJ49G19++SUKCwtrbL9x40a4urri22+/RceOHaFUKvHXX3/hyJEjiIqKgoeHB5o0aYI+ffogPT1ds11AQAAAYNSoUZAkSfP6/q+onnjiCYwcORJvvfUWvL294e7ujrlz50KtVmvaZGVlYejQobC3t0fLli2RmJiIgIAArF27ttZ6icj4GHCIZG7KlClISEjQvP70008xderUKu2ee+45JCcn47PPPkN6ejratGmDQYMG4datW7CyssKECROwadMmrW0SExMRERGBVq1aVemvqKgI/fr1g5OTE/bt24cDBw7AyckJgwcPRklJSbXjFUIgISEBkydPRmBgINq1a4ctW7bUWmdRURFWr16NTz75BKdPn4aXlxcKCgoQFxeH/fv349ChQ2jbti2GDBmCgoICANAEuISEBGRlZWle67J7925cuHABu3fvxmeffYaNGzdi48aNmvWPP/44rl69ij179iA5ORkff/wxsrOzax03EZmImR/2SUQmEhcXJ2JiYsSNGzeEUqkUGRkZIjMzU9jZ2YkbN26ImJgYzdPl79y5IxQKhdi0aZNm+5KSEuHj4yPeeOMNIYQQ6enpQpIkkZmZKYQQoqysTDRv3ly8//77mm0AiG3btgkhhIiPjxft27cX5eXlmvUqlUrY29uLH3/8sdpx79y5U3h6egq1Wi2EEGLNmjUiMjKyxloTEhIEAHHixIka25WWlgpnZ2fxzTff6BxzpZdeekl07dpV8zouLk74+/uL0tJSzbJx48aJ8ePHCyGEOHv2rAAgjhw5oln/xx9/CABizZo1NY6JiEyDR3CIZM7DwwNDhw7FZ599hoSEBAwdOhQeHh5abS5cuAC1Wo3IyEjNMoVCgW7duuHs2bMAgJCQEAQGBmLz5s0AgL179yI7Oxv/93//p3O/x44dw59//glnZ2c4OTnByckJTZs2RXFxMS5cuFDteOPj4zF+/HjY2NgAACZOnIj/9//+H86dO1djnba2tggKCtJalp2djVmzZqFdu3Zo0qQJmjRpgjt37uDSpUs19qVLp06dYG1trXnt7e2tOUJz7tw52NjYIDQ0VLO+TZs2cHNzM3g/RGQcNuYeABGZ3tSpUzFv3jwAwPvvv19lvRACQMU5NPcvv3dZbGwsEhMT8fzzzyMxMRGDBg2qEpYqlZeX4+GHH67ytRYAeHp66tzm1q1b2L59O9RqNT788EPN8rKyMnz66ad4/fXXq63R3t6+yvifeOIJ3LhxA2vXroW/vz+USiUiIiJq/IqsOgqFQuu1JEmaE5kr37/7VbeciEyPR3CIHgCV572UlJRg0KBBVda3adMGtra2OHDggGaZWq3G0aNH0aFDB82ySZMm4eTJkzh27Bi+/vprxMbGVrvP0NBQ/PHHH/Dy8kKbNm20fpo0aaJzm02bNsHX1xe//vorTpw4oflZu3YtPvvsM5SWlhpU9/79+/H0009jyJAh6NSpE5RKJXJycrTaKBQKlJWVGdTv/QIDA1FaWorjx49rlv3555/Iy8urV79EVHcMOEQPAGtra5w9exZnz57V+pqlkqOjI2bPno1nn30WKSkpOHPmDKZPn46ioiJMmzZN065ly5Z45JFHMG3aNJSWliImJqbafcbGxsLDwwMxMTHYv38/MjIysHfvXsyfPx9///23zm3i4+MxduxYdO7cWetn6tSpyMvLw3fffWdQ3W3atMG///1vnD17Fv/v//0/xMbGwt7eXqtNQEAA0tLScO3aNeTm5hrUf6XAwEAMHDgQM2bMwOHDh3H8+HHMmDFD51ElImoYDDhEDwgXFxe4uLhUu/61117DmDFj8NhjjyE0NBR//vknfvzxxyrnkcTGxuLXX3/F6NGjq4SFezk4OGDfvn1o0aIFRo8ejQ4dOmDq1Km4e/euznEcO3YMv/76K8aMGVNlnbOzM6Kjow2+J86nn36K3NxchISE4LHHHtNcBn+vt99+G6mpqTovnTfE559/joceegi9e/fGqFGjMH36dDg7O8POzq7OfRJR3UmCXxITERnd33//DT8/P/z0008YMGCAuYdD9MBhwCEiMoJdu3bhzp076NKlC7KysvDcc8/hypUrOH/+fJUTlInI9HgVFRGREajVaixZsgQXL16Es7MzHnnkEWzatInhhshMeASHiIiIZIcnGRMREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkeww4BAREZHsMOAQERGR7DDgEBERkez8f/MdMi1cdscLAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title(\"User Movie Preference\", size=15) \n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "plt.scatter(X[y == 0, 0], X[y == 0, 1], c='blue', s=100, label='', alpha=0.7)   \n",
    "plt.scatter(X[y == 1, 0], X[y == 1, 1], c='green', s=100, label='', alpha=0.7)\n",
    "# 绘制新数据点，用红色x标记，大小为8\n",
    "# plt.plot(new_user[0, 0], new_user[0, 1], 'rx', markersize=8, markeredgewidth=2, label='°(7')提示：用plt.plot()绘制，用它的参数marker实现不同的标记符号\n",
    "#TODO\n",
    "\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user)#TODO\n",
    "nearest = X[idx[0][0]]#TODO # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "#plt.plot([new_user[0, 0], nearest[0]], [new_user[0, 1], nearest[1]], 'r--', linewidth=2, label='\n",
    "#TODO\n",
    "\n",
    "# 为每个点添加坐标文本  \n",
    "for x, y in zip(X[:, 0], X[:, 1]):\n",
    "    plt.text(x, y+0.1, f'({x}, {y})') \n",
    "\n",
    "# 为新数据点添加坐标文本\n",
    "plt.text(new_user[0,0], new_user[0,1]+0.1, f'({new_user[0,0]}, {new_user[0,1]})')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
